package com.caigou.aop;

import cn.hutool.json.JSONUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.caigou.annotation.OperLog;
import com.caigou.bean.cms.entity.*;
import com.caigou.bean.cms.param.*;
import com.caigou.component.RedisComponent;
import com.caigou.constant.NewsConstant;
import com.caigou.pojo.ArticleData;
import com.caigou.pojo.CatalogConfigData;
import com.caigou.service.*;
import com.caigou.util.IpUtil;
import com.caigou.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import weixin.popular.util.JsonUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class OperLogAspect {
    
    @Resource
    private OperationLogService operationLogService;
    @Resource
    private ArticleService articleService;
    @Resource
    private SystemMessageService systemMessageService;
    @Resource
    private ChannelService channelService;
    @Resource
    private ISurveyService surveyService;
    @Resource
    private SurveyCatalogService catalogService;
    @Resource
    private RedisComponent redisComponent;


    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.caigou.annotation.OperLog)")
    public void operlogPointcut(){
    }

    @AfterReturning(value = "operlogPointcut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OperationLog operationLog = new OperationLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperLog operLog = method.getAnnotation(OperLog.class);
            if (operLog != null) {
                String operModule = operLog.operModule();
                String operType = operLog.operType();
                String operDesc = operLog.operDesc();
                operationLog.setModuleName(operModule); // 操作模块
                operationLog.setOperationType(operType); // 操作类型
                operationLog.setOperationDesc(operDesc); // 操作描述
            }
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;

            operationLog.setOperationMethod(methodName); // 请求方法
            //请求body
            String bodyStr = JSONUtil.toJsonStr(joinPoint.getArgs());
            log.info("bodyStr={}", bodyStr);
            operationLog.setRequestParam(bodyStr);
            if(operLog.operModule().equals(NewsConstant.SYSTEM_MESSAGE_MODULE)){//系统消息
                this.handleSystemMessageOperationLog(joinPoint, operationLog, operLog);
            }

            //处理“频道管理”的操作日志
            if(operLog.operModule().equals(NewsConstant.CHANNEL_MODULE)) {//频道管理操作
                if(methodName.endsWith("ChannelData")||methodName.endsWith("ChannelRecommendData")||methodName.endsWith(".updateChannelDataStickStatus")){//处理ChannelDataParam类型的接口
                    this.handleChannelDataManagementOperationLog(joinPoint, operationLog,methodName);
                }else if(methodName.endsWith("updateChannelConfig")) {//参数类型ChannelParam
                    this.handleChannelManagementOperationLog(joinPoint, operationLog);
                }
            }

            //处理稿件库的操作记录
            if(operLog.operModule().equals(NewsConstant.ARTICLE_MODULE)) {//稿件库的操作
                this.handleArticleOperationLog(joinPoint, operationLog, methodName, bodyStr);
            }

            //处理推荐作者的操作记录
            if(operLog.operModule().equals(NewsConstant.ARTICLE_PROMOTER_MODULE)){//推荐作者的操作
                this.handleArticlePromoterIsForbidOperationLog(joinPoint, operationLog, methodName);
            }

            if (operLog.operModule().equals(NewsConstant.FORM_MODULE)){
                this.handleCatalogConfigOperationLog(joinPoint, operationLog, methodName);
                if (!operationLog.getSaveStatus()){
                    return;
                }
            }

//            // 请求的参数(key-value)
//            Map<String, String> rtnMap = converMap(request.getParameterMap());
//            // 将参数所在的数组转换成json
//            String params = JSON.toJSONString(rtnMap);
//            operartionlog.setRequestParam(params); // 请求参数

            operationLog.setResponseParam(JSON.toJSONString(keys)); // 返回结果
            operationLog.setIp(IpUtil.getIpAddress(request));
            operationLog.setOperationUrl(request.getRequestURI()); // 请求URI
            operationLog.setOperationTime(new Date(System.currentTimeMillis())); // 创建时间
            String userId = (String)SecurityContextHolder.getContext().getAuthentication().getCredentials();//登录用户id
            operationLog.setUserId(userId);

            operationLogService.insertSelective(operationLog);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * 处理系统消息的操作记录
     * @param joinPoint
     * @param operationLog
     * @param operLog
     */
    private void handleSystemMessageOperationLog(JoinPoint joinPoint, OperationLog operationLog, OperLog operLog){
        Object[] objects = joinPoint.getArgs();
        String systemMessageStr = JSONUtil.toJsonStr(objects[0]);
        SystemMessage systemMessageJson = JSONUtil.toBean(systemMessageStr, SystemMessage.class);
        log.info("systemMessageStr={}, id={}, messageTitle={}", systemMessageStr, systemMessageJson.getId(), systemMessageJson.getMessageTitle());
        SystemMessage systemMessageDb = null;
        if(systemMessageJson.getId()==null || operLog.operType().equals(NewsConstant.ADD_OPERATION)){//新增系统消息
            Long messageId = systemMessageJson.getMessageId();
            systemMessageDb = systemMessageService.selectByMessageId(messageId);
            operationLog.setObjectId(systemMessageDb.getId().toString());

        }else{
            operationLog.setObjectId(systemMessageJson.getId().toString());
            if(systemMessageJson.getMessageTitle()==null) {
                systemMessageDb = systemMessageService.selectByPrimaryKey(systemMessageJson.getId());
            }
        }

        if(systemMessageJson.getMessageTitle()==null){
            if(systemMessageDb!=null){
                operationLog.setObjectName(systemMessageDb.getMessageTitle());
            }
        }else {
            operationLog.setObjectName(systemMessageJson.getMessageTitle());
        }
    }

    /**
     * 处理频道数据管理的操作记录
     * @param joinPoint
     * @param operationLog
     */
    private void handleChannelDataManagementOperationLog(JoinPoint joinPoint, OperationLog operationLog, String methodName){
        Object[] objects = joinPoint.getArgs();
        String channelStr = null;
        if(methodName.endsWith(".saveChannelData")){//参数类型为List<ChannelDataParam>
            //TODO
            channelStr = JSONUtil.toJsonStr(objects[0]);
            cn.hutool.json.JSONArray jsonArray = JSONUtil.parseArray(channelStr);
            Object object = jsonArray.get(0);
            channelStr = JSONUtil.toJsonStr(object);
        }else{//参数类型为ChannelDataParam
            channelStr = JSONUtil.toJsonStr(objects[0]);
        }
        ChannelDataParam channelJson = JSONUtil.toBean(channelStr, ChannelDataParam.class);
        String channelId = channelJson.getPlateId();
        Channel channel = channelService.getChannelByPlateId(channelId);
        operationLog.setObjectId(channelId);
        operationLog.setObjectName(channel.getChannelName());
        //覆盖操作日志中的操作描述
        operationLog.setOperationDesc(operationLog.getOperationDesc()+"--"+operationLog.getOperationType()+":"+channel.getChannelName());
        //覆盖操作日志中的模块名称
        String channelIdStr = String.valueOf(channelId);
        String plateId = channelIdStr.substring(0,3);
        Channel rootChannel = channelService.getChannelByPlateId(plateId);
        operationLog.setModuleName(rootChannel.getChannelName()); // 操作模块名称
    }


    /**
     * 处理频道管理的操作记录
     * @param joinPoint
     * @param operationLog
     */
    private void handleChannelManagementOperationLog(JoinPoint joinPoint, OperationLog operationLog){
        Object[] objects = joinPoint.getArgs();
        String channelStr = JSONUtil.toJsonStr(objects[0]);
        ChannelParam channelJson = JSONUtil.toBean(channelStr, ChannelParam.class);
        String channelId = channelJson.getChannelId();
        Channel channel = channelService.getChannelByPlateId(channelId);
        operationLog.setObjectId(channelId);
        operationLog.setObjectName(channel.getChannelName());
        //覆盖操作日志中的操作描述
        operationLog.setOperationDesc(operationLog.getOperationDesc()+"--"+operationLog.getOperationType()+":"+channel.getChannelName());
        //覆盖操作日志中的模块名称
        String channelIdStr = String.valueOf(channelId);
        String plateId = null;
        if(channelIdStr.length()==10){
            plateId= channelIdStr.substring(0,2);//channelIdStr如果是10位的话，一定是采集网站；这种情况下，获取前2位作为channel_value查询条件
        }else{//这种情况是channelIdStr的长度为9的情况
            if(channelIdStr.startsWith("1")){
                plateId= channelIdStr.substring(0,3);//channelIdStr如果是1开始，则为政府采购信息网的频道；这种情况下，获取前3位作为channel_value查询条件
            }else{
                plateId = channelIdStr.substring(0,1);//channelIdStr如果不是1开始，则为采集网站；这种情况下，获取前1位作为channel_value查询条件
            }
        }
        Channel rootChannel = channelService.getChannelByPlateId(plateId);
        operationLog.setModuleName(rootChannel.getChannelName()); // 操作模块
        operationLog.setModuleId(plateId);//模块id，就是t_channel表的channel_value
    }

    /**
     * 处理稿件库的操作记录
     * @param joinPoint
     * @param operationLog
     */
    private void handleArticleOperationLog(JoinPoint joinPoint, OperationLog operationLog, String methodName, String bodyStr){
        if(methodName.endsWith(".updateArticleData")||methodName.endsWith(".saveArticleData")||methodName.endsWith(".updateArticleStatus")||methodName.endsWith(".deleteArticleData")){//更新、增加稿件、下架、删除稿件
            List<Map<String,Object>> list = (List<Map<String,Object>>)JSONUtils.parse(bodyStr);
            Map<String,Object> map = list.get(0);
            //log.info("className={}, methodName={}", className, methodName);
            log.info("map={}", map);
            String articleId = (String)map.get("articleId");
            Article article = null;
            if(articleId!=null){
                article = articleService.loadArticle(articleId);
                if(article!=null){
                    Integer articleCreateSource = article.getArticleCreateSource();
                    if(articleCreateSource.intValue()!=0){//article来源为采集网站，设置moduleName为"采集库"
                        operationLog.setModuleName(NewsConstant.CRAW_MODULE);
                    }
                }
            }
            if(methodName.endsWith(".updateArticleData")||methodName.endsWith(".saveArticleData")) {//更新、增加稿件
                String articleTitle = (String) map.get("articleTitle");
                if (articleTitle != null) {
                    operationLog.setObjectName(articleTitle);
                }
            }
            if(methodName.endsWith(".updateArticleStatus")||methodName.endsWith(".deleteArticleData")){//下架、删除稿件
                if(article!=null){
                    String articleTitle = article.getArticleTitle();
                    if(articleTitle!=null){
                        operationLog.setObjectName(articleTitle);
                    }
                }
            }
        }else if(methodName.endsWith(".loadArticleExportList")){//导出稿件
            operationLog.setObjectName("");
        }
    }

    private void handleArticlePromoterIsForbidOperationLog(JoinPoint joinPoint, OperationLog operationLog, String methodName){
        if(methodName.endsWith(".updateArticlePromoterIsForbid")) {//禁用、启用稿件
            Object[] objects = joinPoint.getArgs();
            String articlePromoterStr = JSONUtil.toJsonStr(objects[0]);
            ArticlePromoterParam articlePromoterJson = JSONUtil.toBean(articlePromoterStr, ArticlePromoterParam.class);
            Integer isForbid = articlePromoterJson.getIsForbid();
            if(isForbid!=null && isForbid==1){
                operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            }else{
                operationLog.setOperationType(NewsConstant.ENABLE_OPERATION);
            }
        }
    }

    /**
     * 启用/禁用目录页
     * @param joinPoint
     * @param operationLog
     * @param methodName
     */
    private void handleCatalogConfigOperationLog(JoinPoint joinPoint, OperationLog operationLog, String methodName){
        Object[] objects = joinPoint.getArgs();
        String paramStr = JSONUtil.toJsonStr(objects[0]);
        if(methodName.endsWith(".updateSurveyCatalogConfigStatus")) {//禁用、启用目录
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            Integer status = param.getStatus();
            SurveyCatalogConfig catalogConfigData = catalogService.loadSurveyCatalogConfig(param.getCatalogId());
            if(status!=null && status!=1){
                operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
                operationLog.setOperationDesc("禁用目录页");
                operationLog.setObjectName(catalogConfigData.getTitle());
            }else{
                operationLog.setOperationType(NewsConstant.ENABLE_OPERATION);
                operationLog.setOperationDesc("启用目录页");
                operationLog.setObjectName(catalogConfigData.getTitle());
            }
        } else if (methodName.endsWith("deleteSurveyCatalogConfig")) {// 状态删除目录页
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            SurveyCatalogConfig catalogConfigData = catalogService.loadSurveyCatalogConfig(param.getCatalogId());
            operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            operationLog.setOperationDesc("删除目录页");
            operationLog.setObjectName(catalogConfigData.getTitle());
        } else if (methodName.endsWith("saveSurveyCatalogConfig")) {// 新增目录页
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            operationLog.setOperationDesc("创建目录页");
            operationLog.setObjectName(param.getTitle());
        } else if (methodName.endsWith("deleteSurveyCatalogConfigData")){// 更新目录页
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            operationLog.setOperationDesc("永久删除目录页");
            operationLog.setObjectName(param.getTitle());
        } else if (methodName.endsWith("updateSurveyCatalogConfigData")){// 删除(物理)目录页
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            SurveyCatalogConfig catalogConfigData = catalogService.loadSurveyCatalogConfig(param.getCatalogId());
            operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            operationLog.setOperationDesc("修改目录页/修改目录页设置");
            operationLog.setObjectName(catalogConfigData.getTitle());
        } else if (methodName.endsWith("updateRecoverCatalogConfig")){// 恢复目录页
            SurveyCatalogParam param = JSONUtil.toBean(paramStr, SurveyCatalogParam.class);
            SurveyCatalogConfig catalogConfigData = catalogService.loadSurveyCatalogConfig(param.getCatalogId());
            operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
            operationLog.setOperationDesc("恢复目录页");
            operationLog.setObjectName(catalogConfigData.getTitle());
        } else if (methodName.endsWith("updateFormEnableStatus")){// 启用、禁用表单
            SurveyFormParam param = JSONUtil.toBean(paramStr, SurveyFormParam.class);
            Integer status = param.getIsEnable();
            SurveyForm surveyForm = surveyService.getFormByFormId(param.getFormId());
            if(status!=null && status!=1){
                operationLog.setOperationType(NewsConstant.DISABLE_OPERATION);
                operationLog.setOperationDesc("禁用表单");
                operationLog.setObjectName(surveyForm.getFormTitle());
            }else{
                operationLog.setOperationType(NewsConstant.ENABLE_OPERATION);
                operationLog.setOperationDesc("启用表单");
                operationLog.setObjectName(surveyForm.getFormTitle());
            }
        } else if (methodName.endsWith("saveForm")){// 创建、修改表单
            SurveyFormParam param = JSONUtil.toBean(paramStr, SurveyFormParam.class);
            String formId = param.getFormId();
            String key = NewsConstant.CREATE_FORM_PRE_KEY+formId;
            String value = redisComponent.get(key);
            if(value!=null&&value.equals("1")){//创建表单
                operationLog.setOperationType(NewsConstant.ADD_OPERATION);
                operationLog.setOperationDesc("创建表单");
                operationLog.setObjectName(param.getFormTitle());
            }else{
                operationLog.setOperationType(NewsConstant.UPDATE_OPERATION);
                operationLog.setOperationDesc("修改表单");
                operationLog.setObjectName(param.getFormTitle());
            }
            if(value!=null) {
                boolean flag = redisComponent.del(key);
                log.info("delete key={} from redis={}", key, flag);
            }
        } else if(methodName.endsWith("deleteForm")||(methodName.endsWith("restoreForm"))){//删除表单或者恢复表单
            SurveyFormParam param = JSONUtil.toBean(paramStr, SurveyFormParam.class);
            SurveyForm surveyForm = surveyService.getFormByFormId(param.getFormId());
            operationLog.setObjectName(surveyForm.getFormTitle());
        } else if(methodName.endsWith("saveFormConfig")){//修改表单配置
            SurveyFormConfigParam param = JSONUtil.toBean(paramStr, SurveyFormConfigParam.class);
            SurveyForm surveyForm = surveyService.getFormByFormId(param.getFormId());
            if(surveyForm!=null) {
                operationLog.setObjectName(surveyForm.getFormTitle());
            }
        } else if (methodName.endsWith("updateSurveyFormOptionSubmitCount")){// 手动修改投票数量
            QueryFormParam param = JSONUtil.toBean(paramStr, QueryFormParam.class);
            SurveyForm surveyForm = surveyService.getFormByFormId(param.getFormId());
            if(surveyForm!=null) {
                operationLog.setObjectName(surveyForm.getFormTitle());
            }
        } else if (methodName.endsWith("loadSurveyFormStatisticsList")){// 表单数据导出
            QueryFormParam param = JSONUtil.toBean(paramStr, QueryFormParam.class);
            if (param.getIsExport() == 1){// 当前为导出数据
                SurveyForm surveyForm = surveyService.getFormByFormId(param.getFormId());
                if(surveyForm!=null) {
                    operationLog.setObjectName(surveyForm.getFormTitle());
                }
            } else {
                operationLog.setSaveStatus(false);
            }
        }
    }
}
